package adapter

import (
	"fmt"
	"github.com/hyperway/pkg/component"
	"github.com/hyperway/pkg/logger"
	"github.com/hyperway/pkg/serialize"
	"github.com/spf13/cast"
	"io"
	"io/ioutil"
	"net/http"
)

type (
	JSONWriter int
)

func NewWriter() component.ServerResponseWriter {
	return new(JSONWriter)
}

func (j *JSONWriter) Write(ctx component.InnerContext, response *component.ServerResponse) {
	headers := response.Headers
	for k, hv := range headers {
		for _, v := range hv {
			ctx.ResponseWriter().Header().Add(k, v)
		}
	}

	if bytes, err := toBytes(response); nil != err {
		j.WriteErr(ctx, &component.ServerError{
			StatusCode: http.StatusInternalServerError,
			ErrorCode:  "SERVER:SERIALIZE/JSON",
			Message:    fmt.Sprintf("failed to serialize response data to json, %s", err),
			Cause:      err,
		})
	} else {
		err := ctx.Write(response.StatusCode, "application/json;charset=UTF-8", bytes)
		if nil != err {
			logger.Errorw("failed to write response data to http response stream", "error", err)
		} else {
			logger.Infow("write response data to http response stream success", "data", string(bytes))
		}
	}
}

func (j *JSONWriter) WriteErr(ctx component.InnerContext, err *component.ServerError) {
	bytes, _ := toBytes(map[string]interface{}{
		"status":  "error",
		"code":    err.ErrorCode,
		"message": err.Message,
		"error":   cast.ToString(err.Error()),
	})
	ctx.Write(500, "application/json;charset=UTF-8", bytes)
}

func toBytes(body interface{}) ([]byte, error) {
	if bytes, ok := body.([]byte); ok {
		return bytes, nil
	} else if str, ok := body.(string); ok {
		return []byte(str), nil
	} else if r, ok := body.(io.Reader); ok {
		if c, ok := r.(io.Closer); ok {
			defer c.Close()
		}
		if bytes, err := ioutil.ReadAll(r); nil != err {
			return nil, fmt.Errorf("SERVER:SERIALIZE/READER: %w", err)
		} else {
			return bytes, nil
		}
	} else {
		if bytes, err := serialize.JSONToBytes(body); nil != err {
			return nil, fmt.Errorf("SERVER:SERIALIZE/JSON: %w", err)
		} else {
			return bytes, nil
		}
	}
}
